﻿using DotNetCommon.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon
{
    /// <summary>
    /// 登录用户信息
    /// </summary>
    /// <remarks>
    /// 该对象储存三类信息：<para></para>
    /// <list type="bullet">
    /// <item>IdString：用户Id字符串，使用string类型表示，可兼容int/long/string/guid等key值，需要在程序启动时注入获取逻辑，参照：<seealso cref="User.RegisterIdString(Func{string}, bool)"/></item>
    /// <item>IsAuthenticated：用户是否登录，默认如果IdString为空则返回false，否则返回true，如果需要自定义，请调用：<seealso cref="User.RegisterIsAuthenticated(Func{bool}, bool)"/>；</item>
    /// <item>Properties：与用户关联的其他属性，需要注入属性容器字典，参照：<seealso cref="User.RegisterPropertiesDictionary(Func{IDictionary{object, object}}, bool)"/>；</item>
    /// </list>
    /// 使用示例：<para></para>
    /// 首先，在StartUp.cs的Configure中注入逻辑：<para></para>
    /// DotNetCommon.User.RegisterIdString(() => httpContextAccessor.HttpContext.User.Identity.Name);<para></para>
    /// DotNetCommon.User.RegisterIsAuthenticated(() => httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);<para></para>
    /// DotNetCommon.User.RegisterPropertiesDictionary(() => (httpContextAccessor.HttpContext?.Items) ?? new Dictionary&lt;object, object>());<para></para>
    /// 然后，在业务代码中使用：<para></para>
    /// var id=DotNetCommon.User.Current.IdString.To&lt;long&gt;();<para></para>
    /// var isAuth=DotNetCommon.User.Current.IsAuthenticated;<para></para>
    /// DotNetCommon.User.Current.SetProperty("age", age);<para></para>
    /// </remarks>
    public class User
    {
        private User()
        {
            UserCreated?.Invoke(this);
        }

        /// <summary>
        /// 用户创建事件
        /// </summary>
        public static event Action<User> UserCreated;

        //异步上下文中保存用户属性
        private static AsyncLocal<User> _asyncUser = new AsyncLocal<User>();

        /// <summary>
        /// 当前登录用户信息
        /// </summary>
        public static User Current
        {
            get
            {
                _asyncUser.Value = _asyncUser.Value ?? new User();
                return _asyncUser.Value;
            }
        }

        #region 用户IsAuthenticated
        private static bool _isCacheAuthenticated = false;
        private static Func<bool> _isAuthenticated;
        private static Func<Task<bool>> _isAuthenticatedTask;

        /// <summary>
        /// 缓存的是否已认证
        /// </summary>
        private bool cacheIsAuthenticated = false;

        /// <summary>
        /// 临时设置的是否认证
        /// </summary>
        private bool? tempIsAuthenticated = false;

        /// <summary>
        /// 是否已认证身份
        /// </summary>
        public bool IsAuthenticated
        {
            get
            {
                if (tempIsAuthenticated != null) return tempIsAuthenticated.Value;
                if (_isCacheAuthenticated && cacheIsAuthenticated) return cacheIsAuthenticated;
                bool b = false;
                if (_isAuthenticated != null)
                {
                    b = _isAuthenticated?.Invoke() ?? false;
                }
                else
                {
                    b = _isAuthenticatedTask?.Invoke()?.Result ?? false;
                }
                if (_isCacheAuthenticated) cacheIsAuthenticated = b;
                return b;
            }
        }

        /// <summary>
        /// 是否已认证身份
        /// </summary>
        public Task<bool> IsAuthenticatedAsync
        {
            get
            {
                if (tempIsAuthenticated != null) return Task.FromResult(tempIsAuthenticated.Value);
                if (_isCacheAuthenticated && cacheIsAuthenticated) return Task.FromResult(cacheIsAuthenticated);
                bool b = false;
                Task<bool> task = null;
                if (_isAuthenticatedTask != null)
                {
                    task = _isAuthenticatedTask?.Invoke();
                }
                else
                {
                    b = _isAuthenticated?.Invoke() ?? false;
                    task = Task.FromResult(b);
                }
                return task?.ContinueWith(t =>
               {
                   if (_isCacheAuthenticated) cacheIsAuthenticated = t.Result;
                   return cacheIsAuthenticated;
               });
            }
        }

        /// <summary>
        /// 注入判断用户是否登录的逻辑
        /// </summary>
        /// <param name="isAuthenticated">判断用户是否登录逻辑</param>
        /// <param name="isCache">是否在异步上下文内缓存结果</param>
        public static void RegisterIsAuthenticated(Func<bool> isAuthenticated, bool isCache = true)
        {
            _isAuthenticated = isAuthenticated;
            _isCacheAuthenticated = isCache;
        }

        /// <summary>
        /// 注入判断用户是否登录的逻辑
        /// </summary>
        /// <param name="isAuthenticatedTask">判断用户是否登录逻辑</param>
        /// <param name="isCache">是否在异步上下文内缓存结果</param>
        public static void RegisterIsAuthenticated(Func<Task<bool>> isAuthenticatedTask, bool isCache = true)
        {
            _isAuthenticatedTask = isAuthenticatedTask;
            _isCacheAuthenticated = isCache;
        }

        /// <summary>
        /// 设置 <c>User.Current.IsAuthenticated</c> 的临时值<br />
        /// </summary>
        public void SetIsAuthenticatedTemp(bool isAuthenticated)
        {
            this.tempIsAuthenticated = isAuthenticated;
        }

        /// <summary>
        /// 取消 <c>User.Current.IsAuthenticated</c> 的临时值<br />
        /// </summary>
        public void RemoveIsAuthenticatedTemp()
        {
            this.tempIsAuthenticated = null;
        }
        #endregion

        #region 用户IdString
        private static bool _isCacheIdString = false;
        private static Func<string> _getIdString;
        private static Func<Task<string>> _getIdStringTask;

        /// <summary>
        /// 缓存的IdString
        /// </summary>
        private string cacheIdString = null;

        private string tempIdString = null;
        private bool isSetTempIdString = false;

        /// <summary>
        /// 用户IdString，使用string兼容多种数据类型: int、long、string
        /// </summary>
        public string IdString
        {
            get
            {
                if (isSetTempIdString) return tempIdString;
                if (_isCacheIdString && cacheIdString.IsNotNullOrEmptyOrWhiteSpace()) return cacheIdString;
                string str = null;
                if (_getIdString != null)
                {
                    str = _getIdString?.Invoke();
                }
                else
                {
                    str = _getIdStringTask?.Invoke()?.Result;
                }
                if (_isCacheIdString) cacheIdString = str;
                return str;
            }
        }

        /// <summary>
        /// 用户IdString，使用string兼容多种数据类型: int、long、string
        /// </summary>
        public Task<string> IdStringAsync
        {
            get
            {
                if (isSetTempIdString) return Task.FromResult(tempIdString);
                if (_isCacheIdString && cacheIdString.IsNotNullOrEmptyOrWhiteSpace()) return Task.FromResult(cacheIdString);
                string str = null;
                Task<string> task = null;
                if (_getIdStringTask != null)
                {
                    task = _getIdStringTask?.Invoke();
                }
                else
                {
                    str = _getIdString?.Invoke();
                    task = Task.FromResult(str);
                }
                return task?.ContinueWith(t =>
                {
                    if (_isCacheIdString) cacheIdString = t.Result;
                    return cacheIdString;
                });
            }
        }

        /// <summary>
        /// 注入获取用户Id的逻辑，同步处理逻辑
        /// </summary>
        /// <param name="getIdString">获取用户Id逻辑，同步版本</param>
        /// <param name="isCache">是否启用缓存（启用缓存后，如果上次获取到了用户Id，那么这次就不再执行获取逻辑，而是直接返回）<para></para>注意：无论是否启用缓存，只要上次没有获取到用户IdString，那么这次就还会执行获取逻辑</param>
        public static void RegisterIdString(Func<string> getIdString, bool isCache = true)
        {
            _getIdString = getIdString;
            _isCacheIdString = isCache;
        }

        /// <summary>
        /// 注入获取用户Id的逻辑，异步处理逻辑
        /// </summary>
        /// <param name="getIdStringTask">获取用户Id逻辑,异步版本</param>
        /// <param name="isCache">是否启用缓存（启用缓存后，如果上次获取到了用户Id，那么这次就不再执行获取逻辑，而是直接返回）<para></para>注意：无论是否启用缓存，只要上次没有获取到用户IdString，那么这次就还会执行获取逻辑</param>
        public static void RegisterIdString(Func<Task<string>> getIdStringTask, bool isCache = true)
        {
            _getIdStringTask = getIdStringTask;
            _isCacheIdString = isCache;
        }

        /// <summary>
        /// 设置 <c>User.Current.IdString</c> 的临时值<br />
        /// </summary>
        public void SetIdStringTemp(string idString)
        {
            this.isSetTempIdString = true;
            this.tempIdString = idString;
        }

        /// <summary>
        /// 取消 <c>User.Current.IsAuthenticated</c> 的临时值<br />
        /// </summary>
        public void RemoveIdStringTemp()
        {
            this.isSetTempIdString = false;
            this.tempIdString = null;
        }
        #endregion

        #region 上下文属性 Properties
        private static bool _isCacheProperties = false;
        private static Func<IDictionary<object, object>> _getProperties;
        private IDictionary<object, object> _props = null;

        /// <summary>
        /// 注入使用的字典逻辑
        /// </summary>
        /// <param name="getProperties"></param>
        /// <param name="isCache"></param>
        public static void RegisterPropertiesDictionary(Func<IDictionary<object, object>> getProperties, bool isCache = true)
        {
            _getProperties = getProperties;
            _isCacheProperties = isCache;
        }

        /// <summary>
        /// 所有属性
        /// </summary>
        /// <returns></returns>
        public IDictionary<object, object> Properties
        {
            get
            {
                if (_isCacheProperties && _props != null) return _props;
                if (_getProperties == null) throw new Exception(@"必须先注册字典获取逻辑,参照:
public void Configure(IApplicationBuilder app)
{
    IHttpContextAccessor httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    DotNetCommon.User.RegisterIdString(() => httpContextAccessor.HttpContext.User.Identity.Name);
    DotNetCommon.User.RegisterIsAuthenticated(() => httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);
    DotNetCommon.User.RegisterPropertiesDictionary(() => (httpContextAccessor.HttpContext?.Items) ?? new Dictionary<object, object>());
}");
                var dic = _getProperties.Invoke();
                if (dic == null) throw new Exception(@"未获取到字典,请检查已注册的字典获取逻辑，参照:
public void Configure(IApplicationBuilder app)
{
    IHttpContextAccessor httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
    DotNetCommon.User.RegisterIdString(() => httpContextAccessor.HttpContext.User.Identity.Name);
    DotNetCommon.User.RegisterIsAuthenticated(() => httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);
    DotNetCommon.User.RegisterPropertiesDictionary(() => (httpContextAccessor.HttpContext?.Items) ?? new Dictionary<object, object>());
}");
                if (_isCacheProperties) _props = dic;
                return dic;
            }
        }

        /// <summary>
        /// 获取当前登录人属性
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public object GetProperty(string key, object defaultValue = null)
        {
            var flag = Properties.TryGetValue(key, out object value);
            if (flag) return value;
            else return defaultValue;
        }

        /// <summary>
        /// 获取当前登录人属性
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns></returns>
        public T GetProperty<T>(string key, T defaultValue = default(T))
        {
            var flag = Properties.TryGetValue(key, out object value);
            if (flag) return value.ToWithDefault(defaultValue);
            else return defaultValue;
        }

        /// <summary>
        /// 是否存在指定的属性
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsProperty(string key)
        {
            return Properties.ContainsKey(key);
        }

        /// <summary>
        /// 设置用户属性(添加或更新)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public void SetProperty(string key, object value)
        {
            lock (Properties)
            {
                if (Properties.ContainsKey(key))
                {
                    Properties[key] = value;
                }
                else
                {
                    Properties.Add(key, value);
                }
            }
        }

        /// <summary>
        /// 删除用户属性
        /// </summary>
        /// <param name="key"></param>
        public void DeleteProperty(string key)
        {
            lock (Properties)
            {
                if (Properties.ContainsKey(key))
                {
                    Properties.Remove(key);
                }
            }
        }
        #endregion
    }
}
